package com.xplay.xpocker.message.action;

import com.xplay.xpocker.business.BusinessEnum;
import com.xplay.xpocker.business.BusinessException;
import com.xplay.xpocker.entity.TMRoomLog;
import com.xplay.xpocker.entity.mahjong.MahjongRoomBuilder;
import com.xplay.xpocker.meta.realize.MahjongRoomEntity;
import com.xplay.xpocker.entity.mahjong.MahjongUserTask;
import com.xplay.xpocker.message.action.mahjong.DiskPartCurrency;
import com.xplay.xpocker.message.MessageToEnum;
import com.xplay.xpocker.meta.MessageContent;
import com.xplay.xpocker.meta.realize.MahjongUserChannel;
import com.xplay.xpocker.observer.SubscriptionSubject;
import com.xplay.xpocker.service.mahjong.IDiskPartService;
import com.xplay.xpocker.service.mahjong.IRoomService;
import com.xplay.xpocker.util.*;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.springframework.transaction.annotation.Transactional;

import javax.swing.*;
import java.util.Arrays;
import java.util.Date;

import static com.xplay.xpocker.util.DictionaryConst.MahjongAction.READ_HOME;

/**
 * @author wanjie
 * @date 2021/3/17 21:42
 */

public abstract class MessageStrategy extends DiskPartCurrency implements IMessageHandler {
    private static String notCheckActions[] = {DictionaryConst.MahjongAction.LOG_OUT, DictionaryConst.MahjongAction.DISSOLUTION,DictionaryConst.MahjongAction.INTERACTIVE};


    @Transactional(rollbackFor = {Exception.class})
    public abstract void doMessage(MahjongUserChannel userChannel, MessageContent message, MahjongRoomEntity roomInfo);


    /**
     * 此方法用于将消息发给所有订阅过后的人
     * <p>
     * 这里 当用户发送一个消息过来后 就会直接移除
     * 但是 并不适用于 碰牌 之后又添加出牌的这种情况
     * 所以当用户有个指令进来过后 又要为用户添加新的指令 这个时候需要将房间清空 并设置 dealWith 为未处理
     *
     * @param ctx
     * @param message
     */

    public void handler(ChannelHandlerContext ctx, MessageContent message) {
        final IDiskPartService diskPartService = SpringContextHolder.getBean(IDiskPartService.class);
        final IRoomService roomService = SpringContextHolder.getBean(IRoomService.class);
        MahjongRoomEntity roomInfo = diskPartService.getRoomInfo(message.getRoomCode());
        BusinessAssertion.isNull(BusinessEnum.ROOM_NOT_FOUND, roomInfo);
        RedisUtil redisUtil = SpringContextHolder.getBean(RedisUtil.class);
        try {
            TMRoomLog roomLog = new TMRoomLog();
            roomLog.setActionName(message.getAction());
            roomLog.setActionUser(getUserId(ctx));
            roomLog.setRoomCode(roomInfo.getCode());
            MahjongUserTask task = diskPartService.userHasTask(roomInfo, getUserId(ctx), message.getAction());
            if (!Arrays.asList(notCheckActions).contains(message.getAction())) {
                BusinessAssertion.isNull("未找到用户可执行操作", task);
            }
            roomLog.setRoomLog(JacksonStringUtil.objectToString(roomInfo, true));
            roomLog.setRoomType(roomInfo.getRoomType());
            roomLog.setCreateDate(new Date());
            roomLog.setActionInfo(JacksonStringUtil.objectToString(message, false));
            roomLog.insert();
            if (task != null) {
                task.setDoTask(message.getAction());
            }
            log.info("user-----{}------{}------body:{}", getUserId(ctx), message.getAction(), JacksonStringUtil.objectToString(message, false));
            //   Assertion.isTrue("用户非法操作", !redisUtil.setNxEx(redisKey, "true", 5000));
            SubscriptionSubject subject = null;
            // 将进来的用户添加到订阅对象和内存中
            if (diskPart.get(message.getRoomCode()) == null) {
                subject = new SubscriptionSubject();
                diskPart.put(message.getRoomCode(), subject);
            }
            MahjongUserChannel userChannel = roomInfo.getUserInfoByUserId(getUserId(ctx));
            userChannel.setUserCtx(ctx);
            // 在执行任何 roomInfo 的操作中 请不要直接对 roomInfo 对象做内存操作
            doMessage(userChannel, message, roomInfo);
            // 这里移除指令 需要判断里面的优先级  以及是否全部处理完成
            diskPartService.removeTask(roomInfo, getUserId(ctx));
            // 里面所有的任务都正常执行的情况下 这个时候才能把数据保存到内存中
            if(!message.getAction().equals(DictionaryConst.MahjongAction.INTERACTIVE)){
                diskPartService.saveRoom(roomInfo);
            }
            log.info("user-----{}------{}------body:{}-----is over", getUserId(ctx), message.getAction(), JacksonStringUtil.objectToString(message, false));
            if (roomInfo.getState() == DictionaryConst.MahjongRoomState.OVER) {
                log.info("game is over,room code {}", roomInfo.getCode());
                redisUtil.del(DictionaryConst.RedisHeader.ROOM_HEADER + roomInfo.getCode());
            }
            MessageToEnum messageType = message.getMessageType();
            subject = diskPart.get(message.getRoomCode());
            message.setUserChannel(roomInfo.getUserInfoByUserId(getUserId(ctx)));
            if (messageType != null && subject != null) {
                if (messageType.equals(MessageToEnum.CURRENT)) {
                    senMessage(ctx, message);
                } else if (messageType.equals(MessageToEnum.ALL)) {
                    subject.notifyMessage(message, null);
                } else {
                    subject.notifyMessage(message, getUserId(ctx));
                }
            }
            // 当房间里面没有用户了  就可以移除对象了
            if (roomInfo.getAllUserInfo().size() == 0) {
                roomService.delRoomInfo(roomInfo.getCode());
            }
        } catch (BusinessException e) {
            e.printStackTrace();
            // 当用户 发生错误操作的时候  推送重新读取房间信息
            senMessage(ctx, new MessageContent(MahjongRoomBuilder.inGame(roomInfo, roomInfo.getUserInfoByUserId(getUserId(ctx))), READ_HOME));
            senMessage(ctx, new MessageContent(e.getMessage(), null, null, e.getStateCode()));
        }
    }
}
